#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2020 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)


import collections
import math
import os
import pandas as pd
import backtrader as bt
from backtrader.utils.py3 import items, iteritems
import shutil
from . import TradeAnalyzer, TimeDrawDown, Calmar, SQN, SharpeRatio, AnnualReturn, MonthlyReturn, AutoDict, AutoOrderedDict
from backtrader.utils.datehelper import *

"""
适合单品种指标分析
"""


class SimpleReport(bt.Analyzer):

    params = (
        ('timeframe', bt.TimeFrame.Days),
        ('compression', 1),
        ('csv', False),
        ('out', None),
        ('rounding', 6)
    )

    def __init__(self):
        dtfcomp = dict(timeframe=self.p.timeframe,
                       compression=self.p.compression)
        self._init_value = self.strategy.broker.get_value()

        # self._trade_analysis = TradeAnalyzer()

        # 最大回撤比例、最大回撤时长
        self._mdd = TimeDrawDown(**dtfcomp)

        # sharp ratio

        self._sharp_ratio = SharpeRatio(**dtfcomp, annualize=True)
        # 收益率(年度/月度)

        self._yearly_rets = AnnualReturn()

        self._monthly_rets = MonthlyReturn()

        # 历史成交
        self._trade_pnl = list()

        # sql
        self._sqn = SQN()

    def notify_trade(self, trade):
        if trade.isclosed:
            self._trade_pnl.append([trade.pnlcomm, trade.commission, trade.size, trade.price, trade.value])

    def cal_trade(self) -> AutoOrderedDict:
        res = AutoOrderedDict()

        # 总盈亏比、平均盈亏比、交易手续费

        # 交易次数
        _trade_count = _win_count = _lost_count = 0

        # 总收益(去掉手续费), 总盈利(去掉手续费), 总亏损(算上手续费)
        _trade_pnl = _win_pnl = _lost_pnl = _trade_comm = 0.0

        # 最长盈利时长, 最长亏损时长
        _win_longest, _lost_longest = 0, 0
        _win_longest_pnl, _lost_longest_pnl = 0, 0

        _current_win_len, _current_lost_len = 0, 0
        _current_win_pnl, _current_lost_pnl = 0, 0

        # 最好单笔盈利、最坏单笔亏损
        _best_win = _worse_lost = 0.0

        for item in self._trade_pnl:
            _trade_count += 1
            _pnlcomm, _comm = item[0], item[1]

            # 总收益
            _trade_pnl += _pnlcomm
            _trade_comm += _comm

            if _pnlcomm > 0:
                _current_win_len += 1
                _current_win_pnl += _pnlcomm
                _current_lost_pnl = 0
                _current_lost_len = 0

                _win_longest = max(_win_longest, _current_win_len)
                _win_longest_pnl = max(_win_longest_pnl, _current_win_pnl)

                _win_count += 1
                _win_pnl += _pnlcomm
                _best_win = max(_best_win, _pnlcomm)
            else:
                _current_lost_len += 1
                _current_lost_pnl += _pnlcomm
                _current_win_len = 0
                _current_win_pnl = 0

                _lost_longest = max(_lost_longest, _current_lost_len)
                _lost_longest_pnl = min(_lost_longest_pnl, _current_lost_pnl)

                _lost_count += 1
                _lost_pnl += _pnlcomm
                _worse_lost = min(_worse_lost, _pnlcomm)

        # 胜率
        _win_rate = _win_count / _trade_count if _trade_count else float('inf')

        # 总盈亏比
        _win_lost_ratio = abs(_win_pnl / _lost_pnl if _lost_pnl else float('inf'))

        # 平均盈利, 平均亏损, 平均盈亏比
        _avg_win = _win_pnl / _win_count if _win_count else float('inf')
        _avg_lost = _lost_pnl / _lost_count if _lost_count else float('inf')
        _win_lost_ratio = abs(_avg_win / _avg_lost if _avg_lost else float('inf'))

        res.win_rate = _win_rate
        res.trade_count = _trade_count
        res.trade_pnl = _trade_pnl
        res.trade_comm = _trade_comm
        res.win_lost_ratio = _win_lost_ratio
        res.avg_win, res.avg_lost = _avg_win, _avg_lost
        res.win_pnl, res.lost_pnl = _win_pnl, _lost_pnl
        res.win_longest, res.lost_longest = _win_longest, _lost_longest
        res.win_longest_pnl, res.lost_longest_pnl = _win_longest_pnl, _lost_longest_pnl
        res.best_win, res.worse_lost = _best_win, _worse_lost
        return res

    def stop(self):
        # 初始净值、最终净值
        _init_value = self._init_value
        _last_value = self.strategy.broker.get_value()
        _last_cash = self.strategy.broker.get_cash()

        bar_len = self.data.datetime.buflen()
        start_time = self.data.datetime.datetime(-(bar_len - 1))
        end_time = self.data.datetime.datetime(0)
        days = (end_time - start_time).days

        # 收益风险比
        _ret = _last_value / _init_value - 1

        self.rets["total_ret"] = _ret
        self.rets["annual_ret"] = _ret / days * 365

        _mdd = self._mdd.get_analysis()["maxdrawdown"] / 100
        _mddlen = self._mdd.get_analysis()["maxdrawdownperiod"]

        self.rets["rets_risk_ratio"] = self.rets["annual_ret"] / _mdd if _mdd else float('inf')
        self.rets["sharp"] = self._sharp_ratio.get_analysis()["sharperatio"]
        self.rets["sqn"] = self._sqn.get_analysis()["sqn"]

        # 最大回撤和最大回撤时长
        self.rets["mdd"] = _mdd
        self.rets["mddlen"] = _mddlen

        # 交易
        _trade = self.cal_trade()
        for k, v in _trade.items():
            self.rets[k] = v

        _month_returns = self._monthly_rets.get_analysis().values()
        self.rets["best_month_ret"] = max(_month_returns)
        self.rets["worst_month_ret"] = min(_month_returns)
        self.rets["avg_month_ret"] = sum(_month_returns) / len(_month_returns)
        self.rets["win_month_count"] = len(list(filter(lambda x: x > 0, _month_returns)))
        self.rets["lost_month_count"] = len(list(filter(lambda x: x <= 0, _month_returns)))

        # 年收益, 月收益
        for date, _returns in self._yearly_rets.get_analysis().items():
            self.rets[date] = _returns

        self.rets["init_value"] = _init_value
        self.rets["last_value"] = _last_value
        self.rets["last_cash"] = _last_cash

        self.rets["start_date"] = int(date2str(start_time, _format_str="%Y%m%d"))
        self.rets["end_date"] = int(date2str(end_time, _format_str="%Y%m%d"))

        if self.p.csv and self.p.out:
            params = self.strategy.p.__dict__.copy()
            params['asset'] = self.data._name
            if self.p.out.startswith("f"):
                out = eval(self.p.out, params)
            else:
                out = self.p.out

            if not os.path.exists(out):
                os.makedirs(out)

            fpath = os.path.join(out, "report.csv")
            import pandas as pd
            df = pd.DataFrame.from_dict(self.rets, orient='index', columns=["value"])
            df.index.name = "ind"
            df["symbol"] = self.data._name
            df = df.reset_index()
            df = df[["symbol", "ind", "value"]]
            df = df.round(decimals=self.p.rounding)
            df.to_csv(fpath, index=False)


